package chapt06.reflection;
// SphereWorld.java
// OpenGL SuperBible
// Demonstrates an immersive 3D environment using actors
// and a camera.  This version adds lights and material properties
// and shadows.
// Program by Richard S. Wright Jr.

import javax.media.opengl.GL2;
import javax.media.opengl.glu.GLU;

import com.jogamp.opengl.util.gl2.GLUT;

import shared.*;

public class Reflection
{
	static GLU glu = new GLU();
	static GLUT glut = new GLUT();
	
	// Light and material data
	static float fLightPos[] = {-100.0f, 100.0f, 50.0f, 1.0f}; // Point source
	static float fLightPosMirror[] = {-100.0f, -100.0f, 50.0f, 1.0f};
	static float fNoLight[] = {0.0f, 0.0f, 0.0f, 0.0f};
	static float fLowLight[] = {0.25f, 0.25f, 0.25f, 1.0f};
	static float fBrightLight[] = {1.0f, 1.0f, 1.0f, 1.0f};
	
	static float yRot = 0.0f; // Rotation angle for animation
	
	//////////////////////////////////////////////////////////////////
	// This function does any needed initialization on the rendering
	// context. 
	protected static void setup(GL2 gl2, int width, int height)
	{
		// Grayish background
		gl2.glClearColor(fLowLight[0], fLowLight[1], fLowLight[2], fLowLight[3]);
		
		// Cull backs of polygons
		gl2.glCullFace(GL2.GL_BACK);
		gl2.glFrontFace(GL2.GL_CCW);
		gl2.glEnable(GL2.GL_CULL_FACE);
		gl2.glEnable(GL2.GL_DEPTH_TEST);
		
		// Setup light parameters
		gl2.glLightModelfv(GL2.GL_LIGHT_MODEL_AMBIENT, fNoLight, 0);
		gl2.glLightfv(GL2.GL_LIGHT0, GL2.GL_AMBIENT, fLowLight, 0);
		gl2.glLightfv(GL2.GL_LIGHT0, GL2.GL_DIFFUSE, fBrightLight, 0);
		gl2.glLightfv(GL2.GL_LIGHT0, GL2.GL_SPECULAR, fBrightLight, 0);
		gl2.glEnable(GL2.GL_LIGHTING);
		gl2.glEnable(GL2.GL_LIGHT0);

		// Mostly use material tracking
		gl2.glEnable(GL2.GL_COLOR_MATERIAL);
		gl2.glColorMaterial(GL2.GL_FRONT, GL2.GL_AMBIENT_AND_DIFFUSE);
		gl2.glMateriali(GL2.GL_FRONT, GL2.GL_SHININESS, 128);
	}

	///////////////////////////////////////////////////////////
	// Draw the ground as a series of triangle strips. The 
	// shading model and colors are set such that we end up 
	// with a black and white checkerboard pattern.
	static void drawGround(GL2 gl2)
	{
		float fExtent = 20.0f;
		float fStep = 0.5f;
		float y = 0.0f;
		float fColor;
		float iStrip, iRun;
		int iBounce = 0;
		
		gl2.glShadeModel(GL2.GL_FLAT);
		for(iStrip = -fExtent; iStrip <= fExtent; iStrip += fStep)
		{
			gl2.glBegin(GL2.GL_TRIANGLE_STRIP);
			for(iRun = fExtent; iRun >= -fExtent; iRun -= fStep)
			{
				if((iBounce % 2) == 0)
					fColor = 1.0f;
				else
					fColor = 0.0f;
				
				gl2.glColor4f(fColor, fColor, fColor, 0.5f);
				gl2.glVertex3f(iStrip, y, iRun);
				gl2.glVertex3f(iStrip + fStep, y, iRun);
				
				iBounce++;
			}
			
			gl2.glEnd();
		}
		gl2.glShadeModel(GL2.GL_SMOOTH);
	}
	
	///////////////////////////////////////////////////////////////////////
	// Draw random inhabitants and the rotating torus/sphere duo
	static void drawWorld(GL2 gl2)
	{
		gl2.glColor3f(1.0f, 0.0f, 0.0f);
		gl2.glPushMatrix();
			gl2.glTranslatef(0.0f, 0.5f, -3.5f);
			
			gl2.glPushMatrix();
				gl2.glRotatef(-yRot * 2.0f, 0.0f, 1.0f, 0.0f);
				gl2.glTranslatef(1.0f, 0.0f, 0.0f);
				glut.glutSolidSphere(0.1f, 17, 9);
			gl2.glPopMatrix();
			
			gl2.glRotatef(yRot, 0.0f, 1.0f, 0.0f);
			GLT.drawTorus(gl2, 0.35f, 0.15f, 61, 37);
		
		gl2.glPopMatrix();
	}
	
	// Called to draw scene
	protected static void render(GL2 gl2, int w, int h)
	{
		float fAspect;
		
		// Prevent a divide by zero, when window is too short
		// (you can't make a window of zero width).
		if(h == 0)
			h = 1;
		
		gl2.glViewport(0, 0, w, h);
		
		fAspect = (float)w / (float)h;
		
		// Reset the coordinate system before modifying
		gl2.glMatrixMode(GL2.GL_PROJECTION);
		gl2.glLoadIdentity();
		
		// Set the clipping volume
		glu.gluPerspective(35.0f, fAspect, 1.0f, 50.0f);
		
		gl2.glMatrixMode(GL2.GL_MODELVIEW);
		gl2.glLoadIdentity();
		gl2.glTranslatef(0.0f, -0.4f, 0.0f);
		
		yRot += 1.0f; // Update Rotation
		
		// Clear the window with current clearing color
		gl2.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
		
		gl2.glPushMatrix();
			// Move light under floor to light the "reflected" world
			gl2.glLightfv(GL2.GL_LIGHT0, GL2.GL_POSITION, fLightPosMirror, 0);
			gl2.glPushMatrix();
				gl2.glFrontFace(GL2.GL_CW); // geometry is mirrored, swap orientation
				gl2.glScalef(1.0f, -1.0f, 1.0f);
				drawWorld(gl2);
				gl2.glFrontFace(GL2.GL_CCW);
			gl2.glPopMatrix();
			
			// Draw the ground transparently over the reflection
			gl2.glDisable(GL2.GL_LIGHTING);
			gl2.glEnable(GL2.GL_BLEND);
			gl2.glBlendFunc(GL2.GL_SRC_ALPHA, GL2.GL_ONE_MINUS_SRC_ALPHA);
			drawGround(gl2);
			gl2.glDisable(GL2.GL_BLEND);
			gl2.glEnable(GL2.GL_LIGHTING);
			
			// Restore correct lighting and draw the world correctly
			gl2.glLightfv(GL2.GL_LIGHT0, GL2.GL_POSITION, fLightPos, 0);
			drawWorld(gl2);
		gl2.glPopMatrix();
		
		// Do the buffer swap
		gl2.glFlush();
	}
}